"""
@project: vcat
@author: kang 
@github: https://github.com/fsksf 
@since: 2021/12/22 10:16 PM
"""
from datetime import datetime
from dataclasses import dataclass
from vcat.core.event import EventType

import logbook
from vcat.core.constant import (
    OrderStatus, OrderType, Exchange, Offset,
    Direction, DataLevel, ContractType, Currency
)
from vcat.utils.api_util import as_table
from vcat.server.obj import (TagMetaInfo, DivMetaInfo, MetaObj)

ACTIVE_STATUSES = (OrderStatus.NEW, OrderStatus.SUBMIT, OrderStatus.ACCEPT,
                   OrderStatus.QUEUED, OrderStatus.PART_TRADED, OrderStatus.CANCELING)


@dataclass
class BarGenArrayParams:
    """
    关于BarGenerator和ArrayManger的设置
    """
    bar_level: DataLevel                    # 生成bar的级别， 分钟、小时、天
    bar_interval: int                       # 生成bar的窗口大小，3分钟， 2小时...
    arr_size: int                           # 设置array的长度，关系到策略的初始化，一定要注意不要过大

    def __post_init__(self):
        self.interval_level_key = str(self.bar_interval) + self.bar_level.name


@dataclass
class MDWeaveParams:
    contract_id: str
    alias: str
    trade_direction: Direction
    trade_coefficient: float = 1


@dataclass
class BaseData(MetaObj):
    """
    Any data object needs a gateway_name as source
    and should inherit base data.
    """
    gateway_name: str


@dataclass
class SubscribeRequest(MetaObj):
    code: str
    exchange: Exchange
    contract_type: ContractType

    def __post_init__(self):
        self.symbol = f'{self.code}.{self.exchange.name}'
        self.cid = f'{self.symbol}.{self.contract_type.name}'


@as_table(page_name='overview')
@dataclass
class AccountData(BaseData, ):
    """
    Account data contains information about balance, frozen and
    available.
    """
    account_id: str
    balance: float = 0
    frozen: float = 0
    currency: Currency = Currency.CNY

    def __post_init__(self):
        self.available = self.balance - self.frozen
        self.aid = f"{self.gateway_name}.{self.account_id}.{self.currency.value}"
        self.id_ = self.aid

    @classmethod
    def meta(cls):
        _meta = DivMetaInfo(
                            name=cls.__name__,
                            title='账户信息',
                            event=EventType.ACCOUNT.name,
                            headers=[
                             TagMetaInfo(name='id_', field='id_', display=False),
                             TagMetaInfo(name='账户', field='account_id'),
                             TagMetaInfo(name='总资金', field='balance'),
                             TagMetaInfo(name='冻结', field='frozen'),
                             TagMetaInfo(name='可用', field='available'),
                             TagMetaInfo(name='币种', field='currency'),
                         ])
        return _meta.to_dict()


@dataclass
class BarData(BaseData):
    code: str
    exchange: Exchange
    contract_type: ContractType
    level: DataLevel
    interval: int
    dt: datetime
    open_price: float
    high_price: float
    low_price: float
    close_price: float
    volume: int

    def __post_init__(self):
        self.symbol = f'{self.code}.{self.exchange.name}'
        self.cid = f'{self.symbol}.{self.contract_type.name}'
        self.interval_level_key = str(self.interval) + self.level.name
        self.qid = f'{self.cid}.{self.interval_level_key}'        # 行情id, code.exchange.type.level.window
        self.id_ = self.qid

    @classmethod
    def meta(cls):
        _meta = DivMetaInfo(
                            name=cls.__name__,
                            title='bar行情',
                            event=EventType.BAR.name,
                            headers=[
                                  TagMetaInfo(name='id_', field='id_', display=False),
                                  TagMetaInfo(name='代码', field='code'),
                                  TagMetaInfo(name='交易所', field='exchange'),
                                  TagMetaInfo(name='类型', field='contract_type'),
                                  TagMetaInfo(name='级别', field='level'),
                                  TagMetaInfo(name='窗口', field='interval'),
                                  TagMetaInfo(name='开', field='open_price'),
                                  TagMetaInfo(name='高', field='high_price'),
                                  TagMetaInfo(name='低', field='low_price'),
                                  TagMetaInfo(name='收', field='close_price'),
                                  TagMetaInfo(name='数量', field='volume'),
                                  TagMetaInfo(name='时间', field='dt'),
                              ])


@as_table(page_name='overview')
@dataclass
class TickData(BaseData):

    code: str
    exchange: Exchange
    contract_type: ContractType
    dt: datetime
    turnover = 0
    volume: int = 0
    open_interest: float = 0
    last_price: float = 0
    last_volume: float = 0
    limit_up: float = 0
    limit_down: float = 0

    open_price: float = 0
    high_price: float = 0
    low_price: float = 0
    pre_close: float = 0

    bid_price_1: float = 0          # 买
    bid_price_2: float = 0
    bid_price_3: float = 0
    bid_price_4: float = 0
    bid_price_5: float = 0

    ask_price_1: float = 0
    ask_price_2: float = 0
    ask_price_3: float = 0
    ask_price_4: float = 0
    ask_price_5: float = 0

    bid_volume_1: float = 0
    bid_volume_2: float = 0
    bid_volume_3: float = 0
    bid_volume_4: float = 0
    bid_volume_5: float = 0

    ask_volume_1: float = 0
    ask_volume_2: float = 0
    ask_volume_3: float = 0
    ask_volume_4: float = 0
    ask_volume_5: float = 0

    def __post_init__(self):
        self.symbol = f'{self.code}.{self.exchange.name}'
        self.cid = f'{self.symbol}.{self.contract_type.name}'
        self.qid = self.cid
        self.id_ = self.cid

    @classmethod
    def meta(cls):
        _meta = DivMetaInfo(title='行情',
                            name=cls.__name__,
                            event=EventType.TICK.name,
                            headers=[
                                  TagMetaInfo(name='id_', field='id_', display=False),
                                  TagMetaInfo(name='代码', field='code'),
                                  TagMetaInfo(name='交易所', field='exchange'),
                                  TagMetaInfo(name='类型', field='contract_type'),
                                  TagMetaInfo(name='最新价', field='last_price'),
                                  TagMetaInfo(name='总成交量', field='volume'),
                                  TagMetaInfo(name='买一价', field='bid_price_1'),
                                  TagMetaInfo(name='买一量', field='bid_volume_1'),
                                  TagMetaInfo(name='卖一价', field='ask_price_1'),
                                  TagMetaInfo(name='卖一量', field='ask_volume_1'),
                              ])
        return _meta.to_dict()


@as_table(page_name='overview')
@dataclass
class OrderData(BaseData):
    """
    订单返回
    """

    code: str
    exchange: Exchange
    contract_type: ContractType
    t_order_id: str                         # 引擎生成的order_id, 立即返回
    b_order_id: str = None                  # broker方面返回的 order_id, 可能会异步返回，较慢
    order_type: OrderType = OrderType.LMT
    direction: Direction = None
    offset: Offset = None
    limit_price: float = None                     # 限价
    avg_price: float = 0
    volume: float = 0
    traded: float = 0
    status: OrderStatus = OrderStatus.NEW
    dt: datetime = None
    reference: str = ""
    strategy_ins_name: str = ''

    def __post_init__(self):
        self.symbol = f'{self.code}.{self.exchange.name}'
        self.cid = f'{self.symbol}.{self.contract_type.name}'
        self.pid = f'{self.cid}.{self.direction.name}'
        self.id_ = f'{self.broker_name}.{self.t_order_id}.{self.b_order_id}'

    def is_active(self) -> bool:
        """
        Check if the order is active.
        """
        return self.status in ACTIVE_STATUSES

    @classmethod
    def meta(cls):
        _meta = DivMetaInfo(
            title='订单',
            name=cls.__name__,
            event=EventType.ORDER.name,
            headers=[
                TagMetaInfo(name='id_', field='id_', display=False),
                TagMetaInfo(name='代码', field='code'),
                TagMetaInfo(name='交易所', field='exchange'),
                TagMetaInfo(name='类型', field='contract_type'),
                TagMetaInfo(name='策略', field='strategy_ins_name'),
                TagMetaInfo(name='订单号', field='t_order_id'),
                TagMetaInfo(name='订单类型', field='order_type'),
                TagMetaInfo(name='多空', field='direction'),
                TagMetaInfo(name='方向', field='offset'),
                TagMetaInfo(name='限价', field='limit_price'),
                TagMetaInfo(name='成本价', field='avg_price'),
                TagMetaInfo(name='成交量', field='volume'),
                TagMetaInfo(name='已成交', field='traded'),
                TagMetaInfo(name='状态', field='status'),
                TagMetaInfo(name='时间', field='dt'),
                TagMetaInfo(name='接口', field='broker_name'),

            ]
        )
        return _meta.to_dict()


@dataclass
class OrderRequest(BaseData):
    code: str
    exchange: Exchange
    contract_type: ContractType
    direction: Direction
    offset: Offset
    order_type: OrderType
    volume: float
    price: float = 0

    def __post_init__(self):
        """"""
        self.symbol = f"{self.code}.{self.exchange.value}"

@dataclass
class CancelRequest(BaseData):
    order_id: str
    code: str
    exchange: Exchange
    reference: str = None

    def __post_init__(self):
        """"""
        self.symbol = f"{self.code}.{self.exchange.value}"


@as_table(page_name='overview')
@dataclass
class TradeData(BaseData):
    """
    Trade data contains information of a fill of an order. One order
    can have several trade fills.
    """

    code: str
    exchange: Exchange
    contract_type: ContractType
    order_id: str
    trade_id: str
    direction: Direction = None
    offset: Offset = Offset.OPEN
    price: float = 0                        # 成交价
    volume: float = 0
    dt: datetime = None
    strategy_ins_name: str = ''

    def __post_init__(self):
        self.symbol = f'{self.code}.{self.exchange.name}'
        self.cid = f'{self.symbol}.{self.contract_type.name}'
        self.id_ = f'{self.broker_name}.{self.trade_id}'

    @classmethod
    def meta(cls,):
        _meta = DivMetaInfo(title='成交记录',
                            name=cls.__name__,
                            event=EventType.TRADE.name,
                            headers=[
                                  TagMetaInfo(name='id_', field='id_', display=False),
                                  TagMetaInfo(name='成交号', field='trade_id', display=True),
                                  TagMetaInfo(name='订单号', field='order_id', display=True),
                                  TagMetaInfo(name='代码', field='code', display=True),
                                  TagMetaInfo(name='交易所', field='exchange', display=True),
                                  TagMetaInfo(name='类型', field='contract_type', display=True),
                                  TagMetaInfo(name='多空', field='direction', display=True),
                                  TagMetaInfo(name='方向', field='offset', display=True),
                                  TagMetaInfo(name='成交价', field='price', display=True),
                                  TagMetaInfo(name='成交量', field='volume', display=True),
                                  TagMetaInfo(name='时间', field='dt', display=True),
                              ])
        return _meta.to_dict()


@dataclass
class OrderRequest(BaseData):
    strategy_ins_name: str
    cid: str
    code: str
    exchange: Exchange
    contract_type: ContractType
    volume: float
    price: float
    direction: Direction
    offset: Offset
    order_type: OrderType
    error_info: str = ''        # 检查未通过原因
    passed = True               # 检查是否通过

    def to_order(self):
        return OrderData(
            broker_name=self.broker_name,
            code=self.code,
            contract_type=self.contract_type,
            exchange=self.exchange,
            t_order_id=None,
            traded=0,
            avg_price=0,
            volume=self.volume,
            limit_price=self.price,
            direction=self.direction,
            offset=self.offset,
            status=OrderStatus.NEW,
            dt=datetime.now(tz=None),
            reference='',
        )


@dataclass
class ContractData(BaseData):
    """
    Contract data contains basic information about each contract traded.
    """

    code: str
    exchange: Exchange
    contract_type: ContractType
    name: str
    price_tick: float               # 下单价格最小变动单位
    lot_size: float = 1             # 下单数量最小变动单位
    min_volume: int = 1             # 下单最小数量
    stop_supported: bool = False    # 是否支持服务器停止单子
    net_position: bool = False      # whether gateway uses net position volume
    history_data: bool = False      # broker是否提供历史数据

    def __post_init__(self):
        self.symbol = f'{self.code}.{self.exchange.name}'
        self.id_ = f'{self.symbol}.{self.contract_type.name}'

    @classmethod
    def meta(cls):
        return {
            'name': '标的列表',
            'event': EventType.CONTRACT.name,
            'headers': [
                {'name': 'id_', 'field': 'id_', 'display': False},
                {'name': '代码', 'field': 'code', 'display': True},
                {'name': '交易所', 'field': 'exchange', 'display': True},
                {'name': '类型', 'field': 'contract_type', 'display': True},
                {'name': '名称', 'field': 'name', 'display': True},
                {'name': '一跳', 'field': 'price_tick', 'display': True},
                {'name': '最小变动数量', 'field': 'lot_size', 'display': True},
                {'name': '最少下单数量', 'field': 'min_volume', 'display': True},
                {'name': '代码', 'field': 'code', 'display': True},
            ]
        }


@as_table(page_name='overview')
@dataclass
class PositionData(BaseData):

    code: str
    exchange: Exchange
    contract_type: ContractType
    direction: Direction
    volume: float = 0
    frozen: float = 0
    avg_price: float = 0
    cur_price: float = 0
    pnl: float = 0
    yd_volume: float = 0

    def __post_init__(self):
        self.symbol = f'{self.code}.{self.exchange.name}'
        self.cid = f'{self.symbol}.{self.contract_type.name}'       # contract_id
        self.id_ = f'{self.cid}.{self.direction.name}'              # position_id

    @classmethod
    def meta(cls):
        _meta = DivMetaInfo(
                            title='持仓信息',
                            name=cls.__name__,
                            event=EventType.POSITION.name,
                            headers=[
                                  TagMetaInfo(name='id_', field='id_', display=False),
                                  TagMetaInfo(name='代码', field='code'),
                                  TagMetaInfo(name='交易所', field='exchange'),
                                  TagMetaInfo(name='类型', field='contract_type'),
                                  TagMetaInfo(name='多空', field='direction'),
                                  TagMetaInfo(name='总仓位', field='volume'),
                                  TagMetaInfo(name='昨仓', field='yd_volume'),
                                  TagMetaInfo(name='平均价', field='avg_price'),
                                  TagMetaInfo(name='当前价', field='cur_price'),
                                  TagMetaInfo(name='盈亏', field='pnl'),
                              ])
        return _meta.to_dict()


@as_table(page_name='log')
@dataclass
class LogData(MetaObj):
    msg: str
    level: str = logbook.INFO
    dt: datetime = None

    def __post_init__(self):
        if self.dt is None:
            self.dt = datetime.now()

    @classmethod
    def meta(cls):
        _meta = DivMetaInfo(title='日志',
                            name=cls.__name__,
                            event=EventType.LOG.name,
                            headers=[
                                TagMetaInfo(name='时间', field='dt'),
                                TagMetaInfo(name='级别', field='level'),
                                TagMetaInfo(name='信息', field='msg')
                            ])
        return _meta.to_dict()


@dataclass
class InitParams:
    key: str
    name: str
    dtype: type
    default: str

    def to_dict(self):
        return {
            'key': self.key,
            'name': self.name,
            'dtype': self.dtype,
            'default': self.default
        }
